package com.ccx.myinterpreter;

import java.util.List;

abstract class Expr {

    public interface Visitor {
        Object visitLiteral(Literal literal);
        Object visitGrouping(Grouping grouping);
        Object visitUnary(Unary unary);
        Object visitBinary(Binary binary);
        Object visitVariable(Variable variable);
        Object visitAssignment(Assignment assignment);
        Object visitCall(Call funCall);
        Object visitGet(Get get);
        Object visitSet(Set set);
        Object visitSuper(Super superExpr);
    }

    static class Literal extends Expr {
        final Object value;

        public Literal(Object value) {
            this.value = value;
        }

        @Override
        Object accept(Visitor visitor) {
            return visitor.visitLiteral(this);
        }
    }

    static class Grouping extends Expr {
        final Expr expr;

        public Grouping(Expr expr) {
            this.expr = expr;
        }

        @Override
        Object accept(Visitor visitor) {
            return visitor.visitGrouping(this);
        }
    }

    static class Unary extends Expr {
        final Token operator;
        final Expr right;

        public Unary(Token operator, Expr right) {
            this.operator = operator;
            this.right = right;
        }

        @Override
        Object accept(Visitor visitor) {
            return visitor.visitUnary(this);
        }
    }

    static class Binary extends Expr {
        final Token operator;
        final Expr left;
        final Expr right;

        public Binary(Token operator, Expr left, Expr right) {
            this.operator = operator;
            this.left = left;
            this.right = right;
        }

        @Override
        Object accept(Visitor visitor) {
            return visitor.visitBinary(this);
        }
    }

    static class Variable extends Expr {
        final Token name;

        public Variable(Token name) {
            this.name = name;
        }

        @Override
        Object accept(Visitor visitor) {
            return visitor.visitVariable(this);
        }
    }

    static class Assignment extends Expr {
        final Token name;
        final Expr expr;

        public Assignment(Token name, Expr expr) {
            this.name = name;
            this.expr = expr;
        }

        @Override
        Object accept(Visitor visitor) {
            return visitor.visitAssignment(this);
        }
    }

    static class Call extends Expr {

        final Expr callee; // function be called
        final List<Expr> args;

        public Call(Expr callee, List<Expr> args) {
            this.callee = callee;
            this.args = args;
        }

        @Override
        Object accept(Visitor visitor) {
            return visitor.visitCall(this);
        }
        
    }

    static class Super extends Expr {
        final Token name;

        public Super(Token name) {
            this.name = name;
        }

        @Override
        Object accept(Visitor visitor) {
            return visitor.visitSuper(this);
        }
        
    }

    static class Get extends Expr {

        final Expr object;
        final Token name;

        public Get(Expr object, Token name) {
            this.object = object;
            this.name = name;
        }
        
        @Override
        Object accept(Visitor visitor) {
            return visitor.visitGet(this);
        }
        
    }

    static class Set extends Expr {
        final Expr object;
        final Token name;
        final Expr value;

        public Set(Expr object, Token name, Expr value) {
            this.object = object;
            this.name = name;
            this.value = value;
        }

        @Override
        Object accept(Visitor visitor) {
            return visitor.visitSet(this);
        }
    }

    abstract Object accept(Visitor visitor);
}
